#pragma once
#include "requestor.h"
#include <unordered_set>

namespace rpc
{
    namespace client
    {
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(Requestor::ptr &req)
                : _requestor(req)
            {
            }
            bool registermethod(const BaseConnection::ptr &conn, const std::string &method, const Address addr)
            {
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->setrId(UUID::uuid());
                req_msg->Sethost(addr);
                req_msg->Setmethod(method);
                req_msg->Setoptype(ServiceOptype::SERVICE_REGISTRY);
                BaseMessage::ptr resp_msg;
                bool ret = _requestor->send(conn, req_msg, resp_msg);
                if (ret == false)
                {
                    ELOG("%s 服务注册失败", method.c_str());
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceReponse>(resp_msg);
                if (service_rsp.get() == nullptr)
                {
                    ELOG("类型向下转换失败");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return false;
            }

        private:
            Requestor::ptr _requestor;
        };
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0) {}
            MethodHost(const std::vector<Address> &host)
                : _hosts(host.begin(), host.end()), _idx(0)
            {
            }
            void appendHost(const Address &addr)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(addr);
            }
            void removerHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _idx++ % _hosts.size();
                return _hosts[pos];
            };
            bool empty()
            {
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _idx;
            std::vector<Address> _hosts;
        };
        class Discover
        {
        public:
            using ptr = std::shared_ptr<Discover>;
            using offlineCallback = std::function<void(const Address &)>;
            Discover(const Requestor::ptr &req, const offlineCallback &cb)
                : _requestor(req), _offline_callback(cb)
            {
            }
            bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->empty() == false)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->setrId(UUID::uuid());
                req_msg->Setmethod(method);
                req_msg->Sethost(host);
                req_msg->Setoptype(ServiceOptype::SERVICE_DISCOVERY);
                BaseMessage::ptr resp_msg;
                bool ret = _requestor->send(conn, req_msg, resp_msg);
                if (ret == false)
                {
                    ELOG("服务发现失败");
                    return false;
                }
                auto server_resp = std::dynamic_pointer_cast<ServiceReponse>(resp_msg);
                if (server_resp.get() == nullptr)
                {
                    ELOG("服务发现失败！响应类型转换失败！");
                    return false;
                }
                if (server_resp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败！%s", errReason(server_resp->rcode()).c_str());
                    return false;
                }
                std::unique_lock<std::mutex> locK(_mutex);
                auto method_host = std::make_shared<MethodHost>(server_resp->host());
                if (method_host->empty())
                {
                    ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                    return false;
                }
                host = method_host->chooseHost();
                _method_hosts[method] = method_host;
                return true;
            }
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto optype = msg->optype();
                std::string method = msg->method();
                std::unique_lock<std::mutex> lock(_mutex);
                if (optype == ServiceOptype::SERVICE_REGISTRY)
                {
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->appendHost(msg->host());
                        _method_hosts[method] = method_host;
                    }
                    else
                    {
                        it->second->appendHost(msg->host());
                    }
                }
                else if (optype == ServiceOptype::SERVICE_OFFLINE)
                {
                    // 3. 下线请求：找到MethodHost，从其中删除一个主机地址
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        return;
                    }
                    it->second->removerHost(msg->host());
                    _offline_callback(msg->host());
                }
            }

        private:
            offlineCallback _offline_callback;
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts;
            Requestor::ptr _requestor;
        };
    }
}